package com.scs.application.core.utils;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
public class UtilCommon {
   /**
    * 判断字符串是否为空
    *
    * @param str
    * @return true str=null || str="null" || str="" || str="undefined"
    */
   public static boolean strIsNull (String str) {
      boolean isNull = false;
      if (str == null) {
         isNull = true;
      } else {
         str = str.replaceAll(" ", "");
         if (str.equals("") || str.equals("null") || str.equals("undefined"))
            isNull = true;
      }
      return isNull;
   }

   /**
    * 为空时，返回newStr,若newStr为null,则返回""
    *
    * @param str
    * @param newStr
    * @return
    */
   public static String getStr (String str, String newStr) {
      if (strIsNull(str)) {
         if (!strIsNull(newStr))
            return newStr;
         return "";
      }
      return str.trim();
   }


   /**
    * 为空时，返回newStr,若newStr为null,则返回""
    *
    * @param str
    * @param newStr
    * @return
    */
   public static String getEntityShort (String entityNameAll) {
      if (!strIsNull(entityNameAll))
         return entityNameAll.substring(entityNameAll.lastIndexOf(".") + 1);
      return "";
   }


   /**
    * 对字符串进行正则表达式校验(区分大小写)
    *
    * @param patternStr 正则表达式字符串
    * @param matcherStr 需要校验的字符串
    * @return true:通过
    */
   public static boolean patternTest (String patternStr, String matcherStr) {
      return Pattern.matches(patternStr, matcherStr);
   }

   /**
    * 截取多长字符串，中文长度x2
    *
    * @param chstring
    * @param length   需要截取的长度
    * @return
    */
   public static String getStringPre (String chstring, int length) {
      if (strIsNull(chstring))
         return chstring;
      if (length >= length(chstring))
         return chstring;

      int num = 0;
      int index = -1;
      StringBuffer sb = new StringBuffer();
      for (int i = 0; i < length(chstring); i++) {
         char c = chstring.charAt(i);
         int move = 0;
         if (isChinese(c))
            move = 2;
         else
            move = 1;
         index += move;
         sb.append(c);
         num += move;
         if (num >= length)
            break;
      }
      return sb.toString();
   }

   /**
    * 获取字符串的字节长度,中文按2字节算
    *
    * @param chstring
    * @return
    */
   public static int length (String chstring) {
      int length = 0;
      if (null == chstring || chstring.equals("") || chstring.length() == 0)
         return 0;
      for (int i = 0; i < chstring.length(); i++) {
         char c = chstring.charAt(i);
         if (isChinese(c))
            length += 2;
         else
            length += 1;
      }
      return length;
   }

   /**
    * 判断字符是不是中文字符
    *
    * @param c
    * @return
    */
   public static boolean isChinese (char c) {
      int ascii = (int) c;
      if (ascii >= 0 && ascii <= 255)
         return false;
      return true;
   }

   /**
    * 判断是否是有效日期 yyyy-MM-dd|yyyy/MM/dd|yyyy.MM.dd|yyyy年MM月dd日|yyyy年MM月dd
    * true:是
    */
   public static boolean isValidDate (String str) {
      boolean isValidDate = true;
      SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
      try {
         // 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期，比如2007/02/29会被接受，并转换成2007/03/01
         format.setLenient(false);
         format.parse(str);
      } catch (ParseException e) {
         // e.printStackTrace();
         // 如果throw java.text.ParseException或者NullPointerException，就说明格式不对
         isValidDate = false;
      }
      if (!isValidDate) {
         try {
            format = new SimpleDateFormat("yyyy/MM/dd");
            // 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期，比如2007/02/29会被接受，并转换成2007/03/01
            format.setLenient(false);
            format.parse(str);
            isValidDate = true;
         } catch (ParseException e) {
            // e.printStackTrace();
            // 如果throw java.text.ParseException或者NullPointerException，就说明格式不对
            isValidDate = false;
         }
      }

      if (!isValidDate) {
         try {
            format = new SimpleDateFormat("yyyy.MM.dd");
            // 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期，比如2007/02/29会被接受，并转换成2007/03/01
            format.setLenient(false);
            format.parse(str);
            isValidDate = true;
         } catch (ParseException e) {
            // e.printStackTrace();
            // 如果throw java.text.ParseException或者NullPointerException，就说明格式不对
            isValidDate = false;
         }
      }

      if (!isValidDate) {
         try {
            format = new SimpleDateFormat("yyyy年MM月dd日");
            // 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期，比如2007/02/29会被接受，并转换成2007/03/01
            format.setLenient(false);
            format.parse(str);
            isValidDate = true;
         } catch (ParseException e) {
            // e.printStackTrace();
            // 如果throw java.text.ParseException或者NullPointerException，就说明格式不对
            isValidDate = false;
         }
      }
      if (!isValidDate) {
         try {
            format = new SimpleDateFormat("yyyy年MM月dd");
            // 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期，比如2007/02/29会被接受，并转换成2007/03/01
            format.setLenient(false);
            format.parse(str);
            isValidDate = true;
         } catch (ParseException e) {
            // e.printStackTrace();
            // 如果throw java.text.ParseException或者NullPointerException，就说明格式不对
            isValidDate = false;
         }
      }
      return isValidDate;
   }

   /**
    * 转换成数据库日期格式 yyyy-MM-dd|yyyy/MM/dd|yyyy.MM.dd|yyyy年MM月dd日|yyyy年MM月dd
    *
    * @param yyyy-MM-dd|yyyy/MM/dd|yyyy.MM.dd|yyyy年MM月dd日
    * @return yyyy-MM-dd格式日期
    */
   public static String toDbDate (String str) {
      boolean isValidDate = true;
      SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
      try {
         // 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期，比如2007/02/29会被接受，并转换成2007/03/01
         format.setLenient(false);
         format.parse(str);
      } catch (ParseException e) {
         // e.printStackTrace();
         // 如果throw java.text.ParseException或者NullPointerException，就说明格式不对
         isValidDate = false;
      }
      if (!isValidDate) {
         try {
            format = new SimpleDateFormat("yyyy/MM/dd");
            // 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期，比如2007/02/29会被接受，并转换成2007/03/01
            format.setLenient(false);
            format.parse(str);
            isValidDate = true;
            str = str.replaceAll("/", "-");
         } catch (ParseException e) {
            // e.printStackTrace();
            // 如果throw java.text.ParseException或者NullPointerException，就说明格式不对
            isValidDate = false;
         }
      }

      if (!isValidDate) {
         try {
            format = new SimpleDateFormat("yyyy.MM.dd");
            // 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期，比如2007/02/29会被接受，并转换成2007/03/01
            format.setLenient(false);
            format.parse(str);
            isValidDate = true;
            str = str.replaceAll("\\.", "-");
         } catch (ParseException e) {
            // e.printStackTrace();
            // 如果throw java.text.ParseException或者NullPointerException，就说明格式不对
            isValidDate = false;
         }
      }

      if (!isValidDate) {
         try {
            format = new SimpleDateFormat("yyyy年MM月dd日");
            // 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期，比如2007/02/29会被接受，并转换成2007/03/01
            format.setLenient(false);
            format.parse(str);
            isValidDate = true;
            str = str.replace("年", "-").replace("月", "-").replace("日", "");
         } catch (ParseException e) {
            // e.printStackTrace();
            // 如果throw java.text.ParseException或者NullPointerException，就说明格式不对
            isValidDate = false;
         }
      }

      if (!isValidDate) {
         try {
            format = new SimpleDateFormat("yyyy年MM月dd");
            // 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期，比如2007/02/29会被接受，并转换成2007/03/01
            format.setLenient(false);
            format.parse(str);
            isValidDate = true;
            str = str.replace("年", "-").replace("月", "-");
         } catch (ParseException e) {
            // e.printStackTrace();
            // 如果throw java.text.ParseException或者NullPointerException，就说明格式不对
            isValidDate = false;
         }
      }

      return str;
   }

   /**
    * GC 即置空对象,让JVM释放该对象
    *
    * @param 对象列表
    */
   public static void GC (Object... obs) {
      for (Object ob : obs)
         ob = null;
   }

   /**
    * 获取n位小数的数字,出现异常返回0
    *
    * @param numStr 数字
    * @param num    几位小数,为0时表示不要小数位（即1.234 返回1）
    */
   public static Object toFloatN (String numStr, int num) {
      String end = "";
      Object ret = "0";
      for (int i = 0; i < num; i++) {
         end += "0";
      }

      if (strIsNull(numStr) || Double.valueOf(numStr) == 0) {
         return "0";
      }
      DecimalFormat df;
      if (num > 0) {
         df = new DecimalFormat("#." + end);
      } else {
         df = new DecimalFormat("#");
      }

      try {
         ret = df.format(Double.parseDouble(numStr));
      } catch (Exception e) {
         // TODO: handle exception
      }
      return ret;
   }

   /**
    * 获取n位小数的数字,出现异常返回0
    *
    * @param numStr 数字
    * @param num    几位小数,为0时表示不要小数位（即1.234 返回1）
    */
   public static double toDoubleN (Object numStr, int num) {
      String end = "";
      Object ret = "0";
      for (int i = 0; i < num; i++) {
         end += "0";
      }

      if (strIsNull(numStr + "") || Double.valueOf(numStr + "") == 0) {
         return 0;
      }
      DecimalFormat df;
      if (num > 0) {
         df = new DecimalFormat("#." + end);
      } else {
         df = new DecimalFormat("#");
      }

      try {
         ret = df.format(Double.parseDouble(numStr + ""));
      } catch (Exception e) {
         // TODO: handle exception
      }
      return Double.parseDouble(ret + "");
   }

   /**
    * 从ResultSet 获取某个key值，
    *
    * @return 异常或者为Null时，如果nullVal为空则返回""，否则返回nullVal
    * @param  rs  结果集
    * @param  key  列名
    * @param  nullVal  为空的时候返回什么
    */
   public static String getRSValue (ResultSet rs, String key, String nullVal) {
      String ret = "";
      try {
         ret = rs.getString(key);
      } catch (Exception e) {
      }
      if (strIsNull(ret) && nullVal != null)
         ret = nullVal;
      return ret;
   }

   /**
    * 将集合以某个特定字符连接起来
    *
    * @return <pre>
    *  集合为空返回""
    *  非空：
    *    如：collection=[1,2] with=","
    *    则返回"1,2"
    * </pre>
    * @param  collection  集合，一般为string类型集合
    * @param  with    连接字符，一般为,
    */
   public static String joinWith (Collection<?> collection, String with) {
      if (collection == null || collection.size() < 1)
         return "";
      return StringUtils.join(collection, with);
   }

   /**
    * 判断字符串是否是json数组
    *
    * @return true:是
    */
   public static boolean isJsonArr (String str) {
      try {
         JSONArray.parseArray(str);
         return true;
      } catch (Exception e) {
         return false;
      }
   }

   /**
    * 将数据集中的数据转成json數組
    *
    * @param resultSet 查询出来的数据集
    * @param onlyValue 只要一列
    */
   public static JSONArray RSToJsonArr (ResultSet resultSet, boolean onlyValue) {
      //        数据集JSON格式
      JSONArray jsonArray = new JSONArray();
      //        数据库中每行的数据对象
      JSONObject rowObj = null;
      try {
         //          ResultSetMetaData 有关 ResultSet 中列的名称和类型的信息。
         ResultSetMetaData rsmd = resultSet.getMetaData();
         //        遍历数据集
         while (resultSet.next()) {
            //          每读取一行，新建对象
            rowObj = new JSONObject();
            //              获取表列数
            int columnCount = rsmd.getColumnCount();
            //            列从1开始，要等于
            for (int i = 1; i <= columnCount; i++) {
               //                获取列名
               String columnName = rsmd.getColumnName(i);
               //                取数据
               String value = resultSet.getString(columnName);
               //                添加到rowObj中
               rowObj.put(columnName, value);
               if (onlyValue) {
                  jsonArray.add(value);
               }
            }
            //                添加到数据集Json
            if (!onlyValue)
               jsonArray.add(rowObj);
         }
      } catch (SQLException e) {
         e.printStackTrace();
      }
      return jsonArray;
   }


   /**
    * 数组去重
    *
    * @param
    */
   public static JSONArray getJsonArrUNQ (JSONArray arr, String fieldName) {
      if (arr == null || arr.size() < 1)
         return new JSONArray();

      JSONArray tmp = new JSONArray(), tmpKey = new JSONArray();
      String keyValue = "";
      for (int i = 0; i < arr.size(); i++) {
         keyValue = arr.getJSONObject(i).getString(fieldName);
         if (!tmpKey.contains(keyValue)) {
            tmp.add(arr.getJSONObject(i));
            tmpKey.add(keyValue);
         }
      }
      return tmp;

   }


   /**
    * 数组只保留单数据 [1,2,3]
    *
    * @param
    */
   public static JSONArray getJsonArrOnly (JSONArray arr, String fieldName) {
      if (arr == null || arr.size() < 1)
         return new JSONArray();

      JSONArray tmp = new JSONArray();
      for (int i = 0; i < arr.size(); i++) {
         tmp.add(arr.getJSONObject(i).getString(fieldName));
      }
      return tmp;
   }

   /**
    * @Description： 将resultset 转换成 bean，对应javabean 字段名字跟数据源名字需要保持一致，同时字段具有set方法
    * <pre>
    *      例如
    *      sql SELECT deptName FROM `t_oprt`
    *      List<Oprt> list2 = UtilCommon.getListFormRs(rs,Oprt.class);
    * </pre>
    */
   public static <T> List<T> getListFormRs (ResultSet resultSet, Class<T> className) {
      List<T> list = new ArrayList<T>();
      Field fields[] = className.getDeclaredFields();
      try {
         while (resultSet.next()) {
            T instance = className.newInstance();
            for (Field field : fields) {
               if (!isExistColumn(resultSet, field.getName()))
                  continue;
               Object result = resultSet.getObject(field.getName());
               Class<?> beanType = field.getType();
               String setMethodName = "set" + firstUpperCase(field.getName());
               // 第一个参数是传进去的方法名称，第二个参数是 传进去的类型；
               Method m = instance.getClass().getMethod(setMethodName, beanType);
               // 第二个参数是传给set方法数据；如果是get方法可以不写
               m.invoke(instance, result);
            }
            list.add(instance);
         }
      } catch (SQLException e) {
         e.printStackTrace();
      } catch (InstantiationException e) {
         e.printStackTrace();
      } catch (IllegalAccessException e) {
         e.printStackTrace();
      } catch (NoSuchMethodException e) {
         e.printStackTrace();
      } catch (InvocationTargetException e) {
         e.printStackTrace();
      }
      return list;
   }

   /**
    * 判断查询结果集中是否存在某列
    *
    * @param rs         查询结果集
    * @param columnName 列名
    * @return true 存在; false 不存咋
    */
   public static boolean isExistColumn (ResultSet rs, String columnName) {
      try {
         if (rs.findColumn(columnName) > 0) {
            return true;
         }
      } catch (SQLException e) {
         return false;
      }

      return false;
   }

   //首写字母变大写
   public static String firstUpperCase (String old) {

      return old.substring(0, 1).toUpperCase() + old.substring(1);
   }

   public static <T> T getBean (ResultSet resultSet, Class<T> className) {
      T instance = null;
      try {
         instance = className.newInstance();
         Field fields[] = className.getDeclaredFields();
         for (Field field : fields) {
            Object result = resultSet.getObject(field.getName());
            boolean flag = field.isAccessible();
            field.setAccessible(true);
            field.set(instance, result);
            field.setAccessible(flag);
         }
      } catch (InstantiationException e) {
         e.printStackTrace();
      } catch (IllegalAccessException e) {
         e.printStackTrace();
      } catch (SQLException e) {
         e.printStackTrace();
      }
      return instance;
   }

   public static boolean isLinux () {
      return System.getProperty("os.name").toLowerCase().contains("linux");
   }

   public static boolean isWindows () {
      return System.getProperty("os.name").toLowerCase().contains("windows");
   }

   /**
    * 处理double精度丢失问题
    *
    * @param value
    * @return
    */
   public static Double doubleFormat (Double value) {
      DecimalFormat doubleFormatter = new DecimalFormat("0.000");
      doubleFormatter.setRoundingMode(RoundingMode.HALF_UP);
      return Double.parseDouble(doubleFormatter.format(value));
   }

   private  static  SerializeConfig config;
   static {
      config = new SerializeConfig();
      config.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
   }


   /**
    * 转换为非驼峰数据
    */
   public static Object getJsonArrNohump (Object list) {
      JSONArray jsonArray = new JSONArray();
      if (list == null || !(list instanceof  List)) {
         return jsonArray;
      }
      return JSONArray.parseArray(JSON.toJSONString(list,config, SerializerFeature.WriteMapNullValue));
   }


   /**
    * 转换为非驼峰数据
    */
   public static Object getJsonNohump (Object obj) {
      JSONArray jsonArray = new JSONArray();
      if (obj == null) {
         return jsonArray;
      }
      return JSON.parse(JSON.toJSONString(obj,config,SerializerFeature.WriteMapNullValue));
   }

   public static Object getBeanFormRs(ResultSet rs, Class<?> clazz) {
      List<Object> list = new ArrayList<>();
      Object obj = null;
      try {
         String nameData,nameField;
         obj = clazz.newInstance();
         Method method;
         Class<?> type;
         // 创建一个clazz对象实例并将其赋给要返回的那个返回值。
         // 获取结果集的数据源
         ResultSetMetaData rsmeta = rs.getMetaData();

         // 获取结果集中的字段数
         int count = rsmeta.getColumnCount();

         // 循环取出个字段的名字以及他们的值并将其作为值赋给对应的实体对象的属性
         for (int i = 0; i < count; i++) {
            // 获取字段名
            nameData = rsmeta.getColumnName(i + 1);
            nameField = StringUtils.underline2Camel(nameData,true);
            if(nameData.indexOf("disabled") != -1) {
               System.out.println("value " + rs.getString(nameData));
            }
            // 利用反射将结果集中的字段名与实体对象中的属性名相对应，由于
            // 对象的属性都是私有的所以要想访问必须加上getDeclaredField(name)和
            Field f = null;
            try {
               f = obj.getClass().getDeclaredField(nameField);
            }catch (NoSuchFieldException e) {
            }
            if (f == null) {
               log.error("字段不存在 {}", nameField);
               continue;
            }
            f.setAccessible(true);

            // 获取字段类型
            type = clazz.getDeclaredField(nameField).getType();
//            log.info("getBeanFormRs 实体字段{} 数据库字段类型{} 数据库字段类型名称{}",nameField,rsmeta.getColumnType(i + 1),rsmeta.getColumnTypeName(i + 1));
            //判断读取数据的类型
            if (type.isAssignableFrom(String.class)) {
               f.set(obj, rs.getString(nameData));
            } else if (type.isAssignableFrom(int.class) || type.isAssignableFrom(Integer.class)) {
               f.set(obj, new Double(UtilNum.getDouble(rs.getObject(nameData))).intValue());
            }  else if (type.isAssignableFrom(double.class) || type.isAssignableFrom(Double.class)) {
               f.set(obj, UtilNum.getDouble(rs.getString(nameData)));
            } else if (type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class)) {
               f.set(obj, StringUtils.toBoolean(rs.getString(nameData), false));
            } else if (type.isAssignableFrom(Date.class)) {
               f.set(obj, rs.getDate(nameData));
            } else if (type.isAssignableFrom(BigDecimal.class)) {
               f.set(obj, rs.getBigDecimal(nameData));
            } else{
               throw new RuntimeException(StrUtil.format("数据类型错误,字段{},实体字段类型需要{}",nameField,type.getName()));
            }
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
      return obj;
   }

   /**
    * 根据路径设置 JSON 对象中的属性值
    * @param jsonObject 目标 JSON 对象
    * @param path       属性路径，如 "a.b.c"
    * @param value      要设置的值
    */
   public static void setValueByPath(JSONObject jsonObject, String path, Object value) {
      if (jsonObject == null || path == null || path.isEmpty()) {
         return;
      }

      String[] parts = path.split("\\.");
      JSONObject current = jsonObject;

      // 遍历路径中的每个部分（除最后一个）
      for (int i = 0; i < parts.length - 1; i++) {
         String key = parts[i];

         // 如果当前键不存在或不是 JSONObject，创建新的 JSONObject
         if (!current.containsKey(key) || !(current.get(key) instanceof JSONObject)) {
            current.put(key, new JSONObject());
         }

         current = current.getJSONObject(key);
      }

      // 设置最终属性值
      String lastKey = parts[parts.length - 1];
      current.put(lastKey, value);
   }

   /**
    * list 只保留中英文
    */
   public static List<String> getListOnlyCn(List<String> list) {
      if (list == null) {
         return  list;
      }
      return   list.stream()
              .map(s -> s.replaceAll("[^a-zA-Z0-9\\u4e00-\\u9fa5\\s,.?!:;-]", "")) // 保留中英文
              .collect(Collectors.toList());
   }

   /**
    * list 查找某个元素出现的位置，只要包含目标元素即可
    */
   public static int getListIndex(List<String> list, String find) {
      int index = 0;
      for(String str : list) {
         if (str.indexOf(find) != -1) {
            return index;
         }
         index ++;
      }
      return -1;
   }
}

